<html>
<head>
  <script type="text/javascript" src="webrtc_test_utilities.js"></script>
  <script type="text/javascript" src="webrtc_test_common.js"></script>
  <script type="text/javascript">
  $ = function(id) {
    return document.getElementById(id);
  };

  window.onerror = function(errorMsg, url, lineNumber, column, errorObj) {
    failTest('Error: ' + errorMsg + '\nScript: ' + url +
             '\nLine: ' + lineNumber + '\nColumn: ' + column +
             '\nStackTrace: ' + errorObj);
  }

  var gPeerConnection = null;
  var gCertificate = null;

  // This test creates and sets three offers, calling setConfiguration in
  // between each offer, expecting an ICE restart to be triggered by the next
  // offer.
  function testSetConfiguration() {
    gPeerConnection = new RTCPeerConnection(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:0});
    // Change ICE candidate pool size, which will succeed before
    // setLocalDescription is called.
    gPeerConnection.setConfiguration(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    // Now test successful cases of setConfiguration. Changes should trigger an
    // ICE restart in the next offer. To do this, first we need to trigger an
    // initial ICE gathering phase and wait until it completes.
    gPeerConnection.onicegatheringstatechange = iceGatheringCallback1;
    createOfferAndSetLocalDescription();
  }

  function iceGatheringCallback1(candidate) {
    if (gPeerConnection.iceGatheringState === 'complete') {
      gPeerConnection.onicegatheringstatechange = iceGatheringCallback2;
      // Policy changed.
      gPeerConnection.setConfiguration(
          {iceServers:[], iceTransportPolicy:'relay', bundlePolicy:'balanced',
           rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
      createOfferAndSetLocalDescription();
    }
  }

  function iceGatheringCallback2(candidate) {
    if (gPeerConnection.iceGatheringState === 'complete') {
      gPeerConnection.onicegatheringstatechange = iceGatheringCallback3;
      // Servers changed.
      gPeerConnection.setConfiguration(
          {iceServers:[{urls:'stun:foo.invalid'}], iceTransportPolicy:'all',
           bundlePolicy:'balanced', rtcpMuxPolicy:'require', certificates:[],
           iceCandidatePoolSize:1});
      createOfferAndSetLocalDescription();
    }
  }

  function iceGatheringCallback3(candidate) {
    // Only wait for 'gathering', since it will take a while for the requests to
    // 'foo.invalid' to time out.
    if (gPeerConnection.iceGatheringState === 'gathering') {
      reportTestSuccess();
    }
  }

  function testSetConfigurationErrors() {
    // Generate certificate so we can test the InvalidModificationError from
    // attempting to change certificates.
    RTCPeerConnection.generateCertificate({ name:'ECDSA', namedCurve:'P-256' })
        .then(function(certificate) {
          gCertificate = certificate;
          continueTestSetConfigurationErrors();
        },
        function() {
          failTest('Failed to generate certificate.');
        }
    );
  }

  // Continued after certificate generated.
  function continueTestSetConfigurationErrors() {
    gPeerConnection = new RTCPeerConnection(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    // If bundlePolicy, rtcpMuxPolicy or certificates are changed, an
    // InvalidModificationError should be thrown.
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'max-bundle', rtcpMuxPolicy:'require',
                  certificates:[], iceCandidatePoolSize:1});
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'balanced', rtcpMuxPolicy:'negotiate',
                  certificates:[]});
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[], iceTransportPolicy:'all',
                  bundlePolicy:'balanced', rtcpMuxPolicy:'require',
                  certificates:[gCertificate], iceCandidatePoolSize:1});
    // Failure to parse URL should result in SyntaxError.
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[{url:'stunnnn:foo.invalid'}],
                  iceTransportPolicy:'all', bundlePolicy:'max-bundle',
                  rtcpMuxPolicy:'require', certificates:[],
                  iceCandidatePoolSize:1});
    // TURN server with missing username should result in InvalidAccessError.
    assertThrows(gPeerConnection.setConfiguration,
                 {iceServers:[{url:'turn:foo.invalid'}],
                  iceTransportPolicy:'all', bundlePolicy:'max-bundle',
                  rtcpMuxPolicy:'require', certificates:[],
                  iceCandidatePoolSize:1});
    // Sanity check that a configuration can be successfully set, and thus
    // there's not something unexpected causing the above exceptions.
    gPeerConnection.setConfiguration(
        {iceServers:[], iceTransportPolicy:'all', bundlePolicy:'balanced',
         rtcpMuxPolicy:'require', certificates:[], iceCandidatePoolSize:1});
    // Lastly: only after applying a local description, changing the candidate
    // pool size is not allowed.
    gPeerConnection.createOffer({offerToReceiveAudio:1})
      .then(function(offer) {
        console.log("Setting offer:\n" + offer.sdp);
        gPeerConnection.setLocalDescription(offer).then(
          function() {
            // Pool size absent, which means it should default to 0, which is
            // different than its current value of 1.
            assertThrows(gPeerConnection.setConfiguration,
                         {iceServers:[], iceTransportPolicy:'all',
                          bundlePolicy:'balanced', rtcpMuxPolicy:'require',
                          certificates:[]});
            reportTestSuccess();
          },
          function() { failTest('Failed to set local description.') }
        );
        },
        function() {
          failTest('Failed to generate offer.')
        }
      );
  }

  function assertThrows(func) {
    try {
      func.apply(arguments.slice(start=1));
      failTest('Expected exception to be thrown by: ' + code);
    } catch (e) {
    }
  }

  // Helper function to create and apply offer.
  function createOfferAndSetLocalDescription() {
    gPeerConnection.createOffer({offerToReceiveAudio:1})
      .then(function(offer) {
        console.log("Setting offer:\n" + offer.sdp);
        gPeerConnection.setLocalDescription(offer).then(
          function() {},
          function() { failTest('Failed to set local description.') }
        );
        },
        function() {
          failTest('Failed to generate offer.')
        }
      );
  }

  </script>
</head>
<body>
</body>
</html>
